/*
 * Ingenic IMP emulation FrameSource.
 *
 * Copyright (C) 2014 Ingenic Semiconductor Co.,Ltd
 * Author: Niky <xianghui.shen@ingenic.cn>
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <linux/videodev2.h>
#include <linux/v4l2-mediabus.h>

#include <constraints.h>
#include <imp/imp_log.h>
#include <imp/imp_system.h>
#include <imp/imp_utils.h>

#include <system/system.h>

#include "emu-v4l2.h"
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
#define TAG "emu-ISP"

typedef enum {
	ISPDEV_STATE_CLOSE,
	ISPDEV_STATE_OPEN,
	ISPDEV_STATE_RUN,
} ISPDevState;

typedef struct {
	/* sensor ops */
	char vi_file[MAX_NAME_LEN];
	int vi_fd;
//	IMPSensorInfo current_sensor;

	/* isp tuning */
	char tuning_file[MAX_NAME_LEN];
	int tuning_fd;
	ISPDevState state;
} EMUISPDev;

static EMUISPDev *gISPdev = NULL;

static inline EMUISPDev * get_ispdev(void)
{
	return gISPdev;
}

int EmuISPOpen(void)
{
	int ret = 0;

	if(gISPdev){
		return 0;
	}

	gISPdev = malloc(sizeof(*gISPdev));
	if (gISPdev == NULL){
		IMP_LOG_ERR(TAG, "Failed to alloc gISPdev!\n");
		return -1;
	}
	memset(gISPdev, 0, sizeof(*gISPdev));

	/* open sensor manager fd */
	sprintf(gISPdev->vi_file, "/dev/v4l-subdev0");

	gISPdev->vi_fd = open(gISPdev->vi_file, O_RDWR, 0);
	if(gISPdev->vi_fd < 0){
		IMP_LOG_ERR(TAG, "Cannot open /dev/v4l-subdev0\n");
		return -1;
	}

	gISPdev->state = ISPDEV_STATE_OPEN;

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] ~~~~~~~\n", __func__,__LINE__);
	return ret;
}

int EmuISPClose(void)
{
	int ret = 0;

	if(!gISPdev)
		return 0;

	if(gISPdev->state > ISPDEV_STATE_OPEN){
		IMP_LOG_ERR(TAG, "Failed to close, because sensor has been deleted!");
		return -1;
	}

	close(gISPdev->vi_fd);
	free(gISPdev);
	gISPdev = NULL;
	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] ~~~~~~~\n", __func__,__LINE__);
	return ret;
}

int EmuISP_AddSensor(IMPSensorInfo *info)
{
	int ret = 0;
	EMUISPDev * ispdev = get_ispdev();

	if(ispdev == NULL){
		IMP_LOG_ERR(TAG, "ISPDEV cannot open\n");
		return -1;
	}

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] ~~~~~~~\n", __func__,__LINE__);
	if(gISPdev->state > ISPDEV_STATE_OPEN){
		IMP_LOG_ERR(TAG, "Sensor is runing, please Call 'EmuISP_DisableSensor' firstly\n");
		return -1;
	}

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] ~~~~~~~\n", __func__,__LINE__);
	if (ioctl(ispdev->vi_fd, VIDIOC_REGISTER_SENSOR, info)) {
		IMP_LOG_ERR(TAG, "VIDIOC_REGISTER_SENSOR(%s) error!\n", info->name);
		return -1;
	}

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] ~~~~~~~\n", __func__,__LINE__);
	/* enum sensor */
	int index = -1;
	struct v4l2_input input;
	input.index = 0;
	while((ret = ioctl(ispdev->vi_fd, VIDIOC_ENUMINPUT, &input)) == 0){
		if(strcmp((const char *)info->name, (const char *)input.name) == 0)
			index = input.index;
		input.index++;
	}
	if(index == -1){
		IMP_LOG_ERR(TAG, "sensor[%s] hasn't been added!\n", info->name);
//		memset(ispdev->current_sensor.name, 0, sizeof(ispdev->current_sensor.name));
		return -1;
	}
	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] ~~~~~~~\n", __func__,__LINE__);
	printf("^^^^ %s [%d] index = %d ^^^^^\n",__func__,__LINE__,index);
	/* select sensor */
	if((ret = ioctl(ispdev->vi_fd, VIDIOC_S_INPUT, &index)) != 0){
		IMP_LOG_ERR(TAG, "Failed to select sensor[%s]!\n", info->name);
		return -1;
	}

	return 0;
}

int EmuISP_EnableSensor(void)
{
	int ret = 0;
	EMUISPDev * ispdev = get_ispdev();

	if(ispdev == NULL){
		IMP_LOG_ERR(TAG, "ISPDEV cannot open\n");
		return -1;
	}
	/* enum sensor */
	int index = -1;

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] ~~~~~~~\n", __func__,__LINE__);
	/* get current sensor */
	if((ret = ioctl(ispdev->vi_fd, VIDIOC_G_INPUT, &index)) != 0){
		IMP_LOG_ERR(TAG, "Failed to Get sensor info!\n");
		return -1;
	}

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] ~~~~~~~\n", __func__,__LINE__);
	if(index == -1){
		IMP_LOG_ERR(TAG, "sensor hasn't been added!\n");
		return -1;
	}

	printf("^^^^ %s [%d] index = %d ^^^^^\n",__func__,__LINE__,index);
	if ((ret = ioctl(ispdev->vi_fd, VIDIOC_STREAMON, &index)) != 0) {
		IMP_LOG_ERR(TAG, "Failed to enable sensor!\n");
		return -1;
	}
	gISPdev->state += ISPDEV_STATE_RUN;
	return 0;
}

int EmuISP_DisableSensor(void)
{
	int ret = 0;
	EMUISPDev * ispdev = get_ispdev();

	if(ispdev == NULL){
		IMP_LOG_ERR(TAG, "ISPDEV cannot open\n");
		return -1;
	}
	/* enum sensor */
	int index = -1;

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] ~~~~~~~\n", __func__,__LINE__);
	/* get current sensor */
	if((ret = ioctl(ispdev->vi_fd, VIDIOC_G_INPUT, &index)) != 0){
		IMP_LOG_ERR(TAG, "Failed to Get sensor info!\n");
		return -1;
	}

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] ~~~~~~~\n", __func__,__LINE__);
	if(index == -1){
		IMP_LOG_ERR(TAG, "sensor hasn't been added!\n");
		return -1;
	}

	printf("^^^^ %s [%d] index = %d ^^^^^\n",__func__,__LINE__,index);
	if ((ret = ioctl(ispdev->vi_fd, VIDIOC_STREAMOFF, &index)) != 0) {
		IMP_LOG_ERR(TAG, "Failed to disable sensor!\n");
		return -1;
	}

	gISPdev->state -= ISPDEV_STATE_RUN;
	return 0;
}

int EmuISP_DelSensor(IMPSensorInfo *info)
{
	int ret = 0;
	int index = -1;
	EMUISPDev * ispdev = get_ispdev();

	if(ispdev == NULL){
		IMP_LOG_ERR(TAG, "ISPDEV cannot open\n");
		return -1;
	}

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] ~~~~~~~\n", __func__,__LINE__);
	if(gISPdev->state > ISPDEV_STATE_OPEN){
		IMP_LOG_ERR(TAG, "Sensor is runing, please Call 'EmuISP_DisableSensor' firstly\n");
		return -1;
	}

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] ~~~~~~~\n", __func__,__LINE__);
	/* clear isp input */
	index = -1;
	if((ret = ioctl(ispdev->vi_fd, VIDIOC_S_INPUT, &index)) != 0){
		IMP_LOG_ERR(TAG, "Failed to select sensor[%s]!\n", info->name);
		return -1;
	}

	IMP_LOG_DBG(TAG, "~~~~~~ %s[%d] ~~~~~~~\n", __func__,__LINE__);
	if (ioctl(ispdev->vi_fd, VIDIOC_RELEASE_SENSOR, info)) {
		IMP_LOG_ERR(TAG, "VIDIOC_REGISTER_SENSOR(%s) error!\n", info->name);
		return -1;
	}
	return 0;
}
